package com.crazy.crazytransactionservice.service.impl;

import com.aliyun.ocr_api20210707.models.RecognizeAllTextResponseBody;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.crazy.crazycommon.common.BaseResponse;
import com.crazy.crazycommon.common.ErrorCode;
import com.crazy.crazycommon.constant.ShareRecordTypeConstant;
import com.crazy.crazycommon.constant.TransactionStatusConstant;
import com.crazy.crazycommon.constant.TransactionTypeConstant;
import com.crazy.crazycommon.exception.BusinessException;
import com.crazy.crazycommon.utils.AliOssUtil;
import com.crazy.crazycommon.utils.ConfirmDateUtils;
import com.crazy.crazycommon.utils.OcrTool;
import com.crazy.crazymodel.model.dto.trade.TradeRequest;
import com.crazy.crazymodel.model.entity.*;
import com.crazy.crazymodel.model.vo.ImageVO;
import com.crazy.crazymodel.model.vo.SubscribeVO;
import com.crazy.crazyserviceclient.service.BusinessFeignClient;
import com.crazy.crazyserviceclient.service.ProductFeignClient;
import com.crazy.crazyserviceclient.service.UserFeignClient;
import com.crazy.crazytransactionservice.context.PositionContext;
import com.crazy.crazytransactionservice.mapper.PositionMapper;
import com.crazy.crazytransactionservice.mapper.ShareRecordMapper;
import com.crazy.crazytransactionservice.mapper.TransactionMapper;
import com.crazy.crazytransactionservice.service.BuyService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import java.io.IOException;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.Date;
import java.util.List;
import java.util.UUID;

/**
 * @Date: 2024-05-06-20:28
 * @Author: Zheng Zeliang
 */

@Service
@Slf4j
public class BuyServiceImpl extends ServiceImpl<TransactionMapper, Transaction> implements BuyService {

    @Resource
    private UserFeignClient userFeignClient;
    @Resource
    private ProductFeignClient productFeignClient;

    @Resource
    private BusinessFeignClient businessFeignClient;
    @Resource
    private TransactionMapper transactionMapper;

    @Resource
    private PositionMapper positionMapper;


    @Resource
    private RedisTemplate redisTemplate;

    @Resource
    private ShareRecordMapper shareRecordMapper;

    /**
     * 根据ID申购
     *
     * @param customerID
     * @param productID
     * @param amount
     * @param bankCardNumber
     * @return
     */
    @Transactional
    @Override
    public SubscribeVO subscribe(int customerID, int productID, BigDecimal amount, String bankCardNumber) {
        Customer customer = null;
        Product product = null;

        try {
            customer = userFeignClient.getById(customerID);
            product = productFeignClient.getById(productID);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }

        // 客户不存在
        if (customer == null) {
            log.info("申购时，客户不存在");
            throw new BusinessException(ErrorCode.NOT_FOUND_ERROR, "客户不存在");
        }
        //产品不存在
        if (product == null) {
            log.info("申购时，产品不存在");
            throw new BusinessException(ErrorCode.NOT_FOUND_ERROR, "产品不存在");
        }
        //申购金额小于1000
        if (amount.compareTo(BigDecimal.valueOf(1000)) < 0) {
            log.info("申购时，金额小于1000，抛出异常");
            throw new BusinessException(ErrorCode.OPERATION_ERROR, "请求金额小于1000");
        }

        SubscribeVO subscribeVO = new SubscribeVO();
        subscribeVO.setRiskOk(true);
        // 风险能力是否匹配
        int customerRiskToleranceScore = customer.getRiskToleranceScore();
        int riskLevel = product.getRiskLevel();
        if (customerRiskToleranceScore < riskLevel) subscribeVO.setRiskOk(false);

        //更新银行卡的冻结金额
        TradeRequest tradeRequest = new TradeRequest();
        tradeRequest.setBankCardId(bankCardNumber);
        tradeRequest.setAmount(amount);
        String payMsg = businessFeignClient.pay(tradeRequest);
        if (payMsg.equals("The balance is insufficient")) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "余额不足");
        } else if (payMsg.equals("not find cardId")) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "银行卡号不存在");
        }


        Date orderTime = null;
        Transaction transaction = null;
        try {
            // 更新交易库表
            orderTime = new Date(System.currentTimeMillis());
            transaction = new Transaction();
            transaction.setCustomerID(customerID);
            transaction.setProductID(productID);
            transaction.setTransactionType(TransactionTypeConstant.SUBSCRIBE);
            transaction.setAmount(amount);
            transaction.setStatus(TransactionStatusConstant.PENDING);
            transaction.setTransactionTime(orderTime);
            transaction.setBankCardNumber(bankCardNumber);
            transactionMapper.insert(transaction);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }

        subscribeVO.setOrderTime(orderTime);
        subscribeVO.setConfirmTime(ConfirmDateUtils.calculateConfirmTime(orderTime));
        subscribeVO.setTransactionId(transaction.getTransactionId());

        return subscribeVO;
    }

    /**
     * 确认申购订单
     *
     * @param transactionId 开启事务管理，因为涉及到两张表的操作
     */
    @Transactional
    @Override
    public void confirmSubscribe(int transactionId) {
        Transaction transaction = transactionMapper.selectById(transactionId);
        if (transaction == null) {
            throw new BusinessException(ErrorCode.NOT_FOUND_ERROR, "确认申购订单不存在");
        }
        int status = transaction.getStatus();
        int transactionType = transaction.getTransactionType();
        if (status != TransactionStatusConstant.PENDING)
            throw new BusinessException(ErrorCode.OPERATION_ERROR, "订单状态错误，确认失败");
        if (transactionType != TransactionTypeConstant.SUBSCRIBE)
            throw new BusinessException(ErrorCode.OPERATION_ERROR, "当前订单非申购订单，确认失败");
        try {
            //冻结的金额解冻
            TradeRequest tradeRequest = new TradeRequest();
            tradeRequest.setBankCardId(transaction.getBankCardNumber());
            tradeRequest.setAmount(transaction.getAmount());
            businessFeignClient.liquidateById(tradeRequest);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }

        // 用户持仓更新
        int customerID = transaction.getCustomerID();
        int productID = transaction.getProductID();
        String bankCardNumber = transaction.getBankCardNumber();
        BigDecimal amount = transaction.getAmount();
        PositionContext positionContext = PositionContext.builder()
                .customerID(customerID)
                .amount(amount)
                .bankCardNumber(bankCardNumber)
                .productID(productID)
                .build();
        boolean isUpdatePosition = updatePosition(positionContext);
        if (!isUpdatePosition) {
            throw new BusinessException(ErrorCode.OPERATION_ERROR, "用户持仓更新失败");
        }

        // 更新交易表状态
        Transaction updateTransaction = new Transaction();
        updateTransaction.setTransactionId(transactionId);
        updateTransaction.setStatus(TransactionStatusConstant.COMPLETE);
        updateTransaction.setCompleteTime(new Date(System.currentTimeMillis()));
        transactionMapper.updateById(updateTransaction);
    }

    public boolean updatePosition(PositionContext positionContext) {
        int customerID = 0;
        int productID = 0;
        BigDecimal amount = null;
        String bankCardNumber = null;
        BigDecimal share = null;
        try {
            customerID = positionContext.getCustomerID();
            productID = positionContext.getProductID();
            amount = positionContext.getAmount();
            bankCardNumber = positionContext.getBankCardNumber();
            //根据产品ID查询当日净值
            BigDecimal networth = businessFeignClient.getNetWorthByProductID(productID);
            // 根据净值，计算份额
            share = amount.divide(networth, 5, RoundingMode.HALF_UP);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        // 根据客户ID、产品ID、银行卡号，找到持仓表中的记录
        QueryWrapper<Position> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("customerID", customerID);
        queryWrapper.eq("productID", productID);
        queryWrapper.eq("bankCardNumber", bankCardNumber);
        Position position = positionMapper.selectOne(queryWrapper);
        //如果持仓记录不存在，就新增
        if (position == null) {
            Position insertPosition = new Position();
            insertPosition.setCustomerID(customerID);
            insertPosition.setProductID(productID);
            insertPosition.setShare(share);
            insertPosition.setAmount(amount);
            insertPosition.setBankCardNumber(bankCardNumber);
            int insert = positionMapper.insert(insertPosition);
            addPositionRecord(share, productID, customerID, bankCardNumber, share);
            return insert > 0;
        }
        //如果持仓记录存在，就更新份额和金额
        else {
            int positionId = position.getPositionId();
            BigDecimal initAmount = position.getAmount();
            BigDecimal updateAmount = initAmount.add(amount);
            BigDecimal initShare = position.getShare();
            BigDecimal updateShare = initShare.add(share);

            Position updatePosition = new Position();
            updatePosition.setPositionId(positionId);
            updatePosition.setAmount(updateAmount);
            updatePosition.setShare(updateShare);
            int update = positionMapper.updateById(updatePosition);
            addPositionRecord(share, productID, customerID, bankCardNumber, updateShare);
            return update > 0;
        }
    }

    /**
     * 新增份额流水
     *
     * @param share
     * @param productID
     * @param customerID
     * @param bankCardNumber
     * @param currentShare
     */
    public void addPositionRecord(BigDecimal share, Integer productID, Integer customerID, String bankCardNumber, BigDecimal currentShare) {
        //将份额变化加入到份额流水表里
        ShareRecord shareRecord = new ShareRecord();
        shareRecord.setChangeShare(share);//负数
        shareRecord.setProductID(productID);
        shareRecord.setCustomerID(customerID);
        shareRecord.setActionType(ShareRecordTypeConstant.BUY);
        shareRecord.setBankCardNumber(bankCardNumber);
        shareRecord.setCurrentShare(currentShare);
        shareRecord.setChangeTime(new Date(System.currentTimeMillis()));
        shareRecordMapper.insert(shareRecord);
    }


    /**
     * 根据图片进行申购
     *
     * @param file
     * @return
     */
    @Override
    public SubscribeVO subscribeByImage(MultipartFile file) {
        String filepath = null;
        try {
            //获取上传文件的原始名称
            String originalFilename = file.getOriginalFilename();
            //获取文件的扩展名
            String extension = originalFilename.substring(originalFilename.lastIndexOf("."));
            //生成最后上传到阿里云的文件名
            String finalName = UUID.randomUUID() + extension;
            //上传到阿里云后，返回文件的路径
            filepath = AliOssUtil.upload(file.getBytes(), finalName);
        } catch (IOException e) {
            log.error("上传文件失败:{}", e);
        }
        if (filepath == null) {
            throw new BusinessException(ErrorCode.OPERATION_ERROR, "图片上传阿里云失败");
        }
        // 使用OCR解析图片
        List<RecognizeAllTextResponseBody.RecognizeAllTextResponseBodyDataSubImagesBlockInfoBlockDetails> ocrResult = null;
        try {
            ocrResult = OcrTool.getOcrResult(filepath);
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        }
        if (ocrResult == null) {
            throw new BusinessException(ErrorCode.OPERATION_ERROR, "图片解析失败");
        }
        String customerName = ocrResult.get(4).getBlockContent();
        String bankCardNumber = ocrResult.get(10).getBlockContent();
        String idNumber = ocrResult.get(12).getBlockContent();
        String productName = ocrResult.get(17).getBlockContent();
        String productCode = ocrResult.get(19).getBlockContent();
        String amountStr = ocrResult.get(21).getBlockContent();
        BigDecimal amount = new BigDecimal(amountStr);

        Customer customer = userFeignClient.getByNameAndIdNumber(customerName, idNumber);
        Product product = productFeignClient.getByCode(productCode);
        // 客户不存在
        if (customer == null) {
            log.info("图片申购时，客户不存在");
            throw new BusinessException(ErrorCode.NOT_FOUND_ERROR, "客户不存在");
        }
        //产品不存在
        if (product == null) {
            log.info("图片申购时，产品不存在");
            throw new BusinessException(ErrorCode.NOT_FOUND_ERROR, "产品不存在");
        }
        //申购金额小于1000
        if (amount.compareTo(BigDecimal.valueOf(1000)) < 0) {
            log.info("申购时，金额小于1000，抛出异常");
            throw new BusinessException(ErrorCode.OPERATION_ERROR, "请求金额小于1000");
        }

        SubscribeVO subscribeVO = new SubscribeVO();
        subscribeVO.setRiskOk(true);
        // 风险能力是否匹配
        int customerRiskToleranceScore = customer.getRiskToleranceScore();
        int riskLevel = product.getRiskLevel();
        if (customerRiskToleranceScore < riskLevel) subscribeVO.setRiskOk(false);

        //更新银行卡的冻结金额
        TradeRequest tradeRequest = new TradeRequest();
        tradeRequest.setBankCardId(bankCardNumber);
        tradeRequest.setAmount(amount);
        String payMsg = businessFeignClient.pay(tradeRequest);
        if (payMsg.equals("The balance is insufficient")) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "余额不足");
        } else if (payMsg.equals("not find cardId")) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "银行卡号不存在");
        }


        Date orderTime = null;
        Transaction transaction = null;
        try {
            // 更新交易库表
            orderTime = new Date(System.currentTimeMillis());
            transaction = new Transaction();
            transaction.setCustomerID(customer.getCustomerID());
            transaction.setProductID(product.getProductID());
            transaction.setTransactionType(TransactionTypeConstant.SUBSCRIBE);
            transaction.setAmount(amount);
            transaction.setStatus(TransactionStatusConstant.PENDING);
            transaction.setTransactionTime(orderTime);
            transaction.setBankCardNumber(bankCardNumber);
            transactionMapper.insert(transaction);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }

        subscribeVO.setOrderTime(orderTime);
        subscribeVO.setConfirmTime(ConfirmDateUtils.calculateConfirmTime(orderTime));
        subscribeVO.setTransactionId(transaction.getTransactionId());

        return subscribeVO;

    }


    /**
     * 解析图片
     *
     * @param file
     * @return
     */
    @Override
    public ImageVO anaylizeImage(MultipartFile file) {
        String filepath = null;
        try {
            //获取上传文件的原始名称
            String originalFilename = file.getOriginalFilename();
            //获取文件的扩展名
            String extension = originalFilename.substring(originalFilename.lastIndexOf("."));
            //生成最后上传到阿里云的文件名
            String finalName = UUID.randomUUID() + extension;
            //上传到阿里云后，返回文件的路径
            filepath = AliOssUtil.upload(file.getBytes(), finalName);
        } catch (IOException e) {
            log.error("上传文件失败:{}", e);
        }
        if (filepath == null) {
            throw new BusinessException(ErrorCode.OPERATION_ERROR, "图片上传阿里云失败");
        }
        // 使用OCR解析图片
        List<RecognizeAllTextResponseBody.RecognizeAllTextResponseBodyDataSubImagesBlockInfoBlockDetails> ocrResult = null;
        try {
            ocrResult = OcrTool.getOcrResult(filepath);
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        }
        if (ocrResult == null) {
            throw new BusinessException(ErrorCode.OPERATION_ERROR, "图片解析失败");
        }
        String customerName = ocrResult.get(4).getBlockContent();
        String bankCardNumber = ocrResult.get(10).getBlockContent();
        String idNumber = ocrResult.get(12).getBlockContent();
        String productName = ocrResult.get(17).getBlockContent();
        String productCode = ocrResult.get(19).getBlockContent();
        String amountStr = ocrResult.get(21).getBlockContent();
        String fundAccout = ocrResult.get(6).getBlockContent();
        String cardType = ocrResult.get(8).getBlockContent();
        String phoneNumber = ocrResult.get(14).getBlockContent();
        String sbsTime = ocrResult.get(22).getBlockContent().substring(8);

        BigDecimal amount = new BigDecimal(amountStr);
        Integer customerID = null;
        Integer productID = null;

        Customer customer = userFeignClient.getByNameAndIdNumber(customerName, idNumber);
        Product product = productFeignClient.getByCode(productCode);
        // 客户不存在
        if (customer == null) {
            log.info("解析图片，客户不存在");
            customerID = null;
        } else {
            customerID = customer.getCustomerID();
        }
        //产品不存在
        if (product == null) {
            log.info("解析图片，产品不存在");
            productID = null;
        } else {
            productID = product.getProductID();
        }

        ImageVO imageVO = new ImageVO();
        imageVO.setCustomerID(customerID);
        imageVO.setProductID(productID);
        imageVO.setCustomerName(customerName);
        imageVO.setFundAccount(fundAccout);
        imageVO.setCardType(cardType);
        imageVO.setBankcardNumber(bankCardNumber);
        imageVO.setIdNumber(idNumber);
        imageVO.setPhoneNumber(phoneNumber);
        imageVO.setProductName(productName);
        imageVO.setProductCode(productCode);
        imageVO.setAmount(amount);
        imageVO.setSubsTime(sbsTime);
        return imageVO;
    }
}
